/+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 + SoulFind - Free SoulSeek server                                           +
 +                                                                           +
 + Copyright (C) 2005 SeeSchloss <seeschloss@seeschloss.org>                 +
 +                                                                           +
 + This  program  is free software ; you can  redistribute it  and/or modify +
 + it under  the  terms of  the GNU General Public License  as published  by +
 + the  Free  Software  Foundation ;  either  version  2 of  the License, or +
 + (at your option) any later version.                                       +
 +                                                                           +
 + This  program  is  distributed  in the  hope  that  it  will  be  useful, +
 + but   WITHOUT  ANY  WARRANTY ;  without  even  the  implied  warranty  of +
 + MERCHANTABILITY   or   FITNESS   FOR   A   PARTICULAR  PURPOSE.  See  the +
 + GNU General Public License for more details.                              +
 +                                                                           +
 + You  should  have  received  a  copy  of  the  GNU General Public License +
 + along   with  this  program ;  if  not,  write   to   the  Free  Software +
 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA +
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/


module messages;

import defines;

import std.stream, std.outbuffer, std.cstream;
import std.format, std.md5;
import std.date;
import std.string;

private import message_codes;

class Message
	{	// Server message
	uint code;
	OutBuffer b;

	ubyte[] toBytes () {return b.toBytes ();}

	this (uint code)
		{
		b = new OutBuffer ();
		this.code = code;
		writei (code);
		}

	void writei (int i)
		{
		try {b.write (i);}
		catch (WriteException e) {derr.writefln (e, " when trying to send an int : ", i);}
		}
	void writeUint (uint i)
		{
		try {b.write (i);}
		catch (WriteException e) {derr.writefln (e, " when trying to send an uint : ", i);}
		}
	void writeb (byte o)
		{
		try {b.write (o);}
		catch (WriteException e) {derr.writefln (e, " when trying to send a byte : ", o);}
		}
	
	void writes (char[] s)
		{
		try 
			{
			b.write (s.length);
			b.write (s);
			}
		catch (WriteException e) {derr.writefln (e, " when trying to send a string : ", s, "(", s.length, ")");}
		}

	uint length;
	Stream s;

	this (Stream s)
		{
		this.s = s;
		}

	int readi ()
		{ // read an int
		int i;
		try {s.read (i);}
		catch (ReadException e)
			{
			derr.writefln ("message code ", code, ", length ", length, " trying to read an int : ", e);
			i = 0;
			}
		return i;
		}
	uint readUint ()
		{ // read an int
		uint i;
		try {s.read (i);}
		catch (ReadException e)
			{
			derr.writefln ("message code ", code, ", length ", length, " trying to read an uint : ", e);
			i = 0;
			}
		return i;
		}
	byte readb ()
		{ // read a byte
		byte b;
		try {s.read (b);}
		catch (ReadException e)
			{
			derr.writefln ("message code ", code, ", length ", length, " trying to read a byte : ", e);
			b = 0;
			}
		return b;
		}
	
	char[] reads ()
		{ // read a string
		uint    slen;
		char[] str;
		try
			{
			slen = readUint ();
			str = s.readString (slen);
			}
		catch (ReadException e)
			{
			derr.writefln ("message code ", code, ", length ", length, " trying to read a string : ", e);
			str = "";
			}
		return str;
		}
	}

class ULogin : Message
	{		// New login
	char[] name;	// user name
	char[] pass;	// user password
	uint    vers;	// client version (198 for nicotine and museek, 180 for pyslsk)

	this (Stream s)
		{
		super (s);

		name = reads ();
		pass = reads ();
		vers = readi ();
		}

	this (char[] name, char[] pass, uint  vers)
		{
		super (Login);

		writes (name);
		writes (pass);
		writei (vers);
		}
	}

class USetWaitPort : Message
	{		// A client is telling us which port it is listening on
	uint  port;	// port number
	this (Stream s)
		{
		super (s);

		port = readi ();
		}
	
	this (uint  port)
		{
		super (SetWaitPort);

		writei (port);
		}
	}
	
class SWishlistInterval	 : Message
	{
	this ()
		{
		super(WishlistInterval);
		
		writei (WishlistInterval);
		}
	}
	
class UGetPeerAddress : Message
	{		// A client is asking for someone's address
	char[] user;	// name of the user to get the address of
	
	this (Stream s)
		{
		super (s);

		user = reads ();
		}
	
	this (char[] user)
		{
		super (GetPeerAddress);

		writes (user);
		}
	}

class UUserExists : Message
	{		// A client wants to know if some user exists ?
	char[] user;	// name of the user

	this (Stream s)
		{
		super (s);

		user = reads ();
		}
	
	this (char[] user)
		{
		super (UserExists);

		writes (user);
		}
	}

class UAddUser : Message
	{		// A client wants to watch someone else
	char[] user;	// name of the user to watch

	this (Stream s)
		{
		super (s);

		user = reads ();
		}
	
	this (char[] user)
		{
		super (AddUser);

		writes (user);
		}
	}

class UGetUserStatus : Message
	{		// A client wants to know the status of someone
	char[] user;	// name of the user
	
	this (Stream s)
		{
		super (s);

		user = reads ();
		}
	
	this (char[] user)
		{
		super (GetUserStatus);

		writes (user);
		}
	}

class USayChatroom : Message
	{		// A client wants to say something in a chatroom
	char[] room;	// room to talk in
	char[] string;	// what to say
	
	this (Stream s)
		{
		super (s);
		
		room   = reads ();
		string = reads ();
		}
	
	this (char[] room, char[] string)
		{
		super (SayChatroom);

		writes (room);
		writes (string);
		}
	}

class UJoinRoom : Message
	{		// Client wants to join a room
	char[] room;	// room the client wants to join

	this (Stream s)
		{
		super (s);

		room = reads ();
		}
	
	this (char[] room)
		{
		super (JoinRoom);

		writes (room);
		}
	}

class ULeaveRoom : Message
	{		// Client wants to leave a room
	char[] room;	// room the client wants to leave

	this (Stream s)
		{
		super (s);

		room = reads ();
		}
	
	this (char[] room)
		{
		super (LeaveRoom);

		writes (room);
		}
	}

class UConnectToPeer : Message
	{		// Client cannot connect to another one and wants us to ask the other to connect to him
	uint  token;	// connection token
	char[] user;	// user name
	char[] type;	// connection type ("F" if for a file transfers, "P" otherwise)

	this (Stream s)
		{
		super (s);

		token = readi ();
		user  = reads ();
		type  = reads ();
		}
	
	this (uint  token, char[] user, char[] type)
		{
		super (ConnectToPeer);

		writei (token);
		writes (user);
		writes (type);
		}
	}

class UMessageUser : Message
	{		// Client wants to send a private message
	char[] user;	// user to send the message to
	char[] message; // message content

	this (Stream s)
		{
		super (s);

		user    = reads ();
		message = reads ();
		}
	
	this (char[] user, char[] message)
		{
		super (MessageUser);

		writes (user);
		writes (message);
		}
	}

class UMessageAcked : Message
	{		// Client acknowledges a private message
	uint  id;		// message id
	
	this (Stream s)
		{
		super (s);

		id = readi ();
		}
	
	this (uint  id)
		{
		super (MessageAcked);

		writei (id);
		}
	}

class UFileSearch : Message
	{		// Client makes a filesearch
	uint  token;	// search token
	char[] strng;	// search string

	this (Stream s)
		{
		super (s);

		token = readi ();
		strng = reads ();
		}
	
	this (uint  token, char[] string)
		{
		super (FileSearch);

		writei (token);
		writes (string);
		}
	}

class USetStatus : Message
	{		// Client sets its status
	int status;	// -1 : Unknown - 0 : Offline - 1 : Away - 2 : Online

	this (Stream s)
		{
		super (s);

		status = readi ();
		}
	
	this (int status)
		{
		super (SetStatus);

		writei (status);
		}
	}

class USendSpeed : Message
	{		// Client reports a transfer speed
	char[] user;	// user name
	uint     speed;   // speed
	
	this (Stream s)
		{
		super (s);

		user  = reads ();
		speed = readi ();
		}
	
	this (char[] user, uint  speed)
		{
		super (SendSpeed);

		writes (user);
		writei (speed);
		}
	}

class USendUploadSpeed : Message
	{		// Client reports it's own transfer speed
	uint     speed;   // speed
	
	this (Stream s)
		{
		super (s);

		speed = readi ();
		}
	
	this (uint  speed)
		{
		super (SendUploadSpeed);

		writei (speed);
		}
	}
	
class USharedFoldersFiles : Message
	{		// Client tells us how many files and folder it is sharing
	uint  nb_folders;	// number of folders
	uint  nb_files;	// number of files

	this (Stream s)
		{
		super (s);

		nb_folders = readi ();
		nb_files   = readi ();
		}
	
	this (uint  nb_folders, uint  nb_files)
		{
		super (SharedFoldersFiles);

		writei (nb_folders);
		writei (nb_files);
		}
	}

class UGetUserStats : Message
	{		// Client wants the stats of someone
	char[] user;	// user name

	this (Stream s)
		{
		super (s);

		user = reads ();
		}
	
	this (char[] user)
		{
		super (GetUserStats);

		writes (user);
		}
	}

class UUserSearch : Message
	{		// Client wants to send searches to his buddies...
	char[] user;	// user to send the search to (yes, there is one message to the server
			// sent for each buddy... how efficient [:kiki])
	uint     token;	// search token
	char[] query;	// search string

	this (Stream s)
		{
		super (s);

		user  = reads ();
		token = readi ();
		query = reads ();
		}
	
	this (char[] user, uint  token, char[] query)
		{
		super (RoomSearch);

		writes (user);
		writei (token);
		writes (query);
		}
	}

class UAddThingILike : Message
	{		// Client likes thing
	char[] thing;	// thing (s)he likes

	this (Stream s)
		{
		super (s);

		thing = reads ();
		}
	
	this (char[] thing)
		{
		super (AddThingILike);

		writes (thing);
		}
	}

class URemoveThingILike : Message
	{		// Client doesn't like thing anymore
	char[] thing;	// the thing

	this (Stream s)
		{
		super (s);

		thing = reads ();
		}
	
	this (char[] thing)
		{
		super (RemoveThingILike);

		writes (thing);
		}
	}

class UAddThingIHate : Message
	{		// Client hates thing
	char[] thing;	// thing (s)he likes

	this (Stream s)
		{
		super (s);

		thing = reads ();
		}
	
	this (char[] thing)
		{
		super (AddThingIHate);

		writes (thing);
		}
	}

class URemoveThingIHate : Message
	{		// Client doesn't hate thing anymore
	char[] thing;	// the thing

	this (Stream s)
		{
		super (s);

		thing = reads ();
		}
	
	this (char[] thing)
		{
		super (RemoveThingIHate);

		writes (thing);
		}
	}

class UAddToPrivileged : Message
	{		// An admin gives privileges to an user
	char[] user;	// user to give the privileges to
	uint     time;	// privileges credits

	this (Stream s)
		{
		super (s);

		user = reads ();
		time = readi ();
		}
	
	this (char[] user, uint  time)
		{
		super (AddToPrivileged);

		writes (user);
		writei (time);
		}
	}
class UUserInterests : Message
	{		// A user wants to know this user's
	char[] user;	// likes and hates

	this (Stream s)
		{
		super (s);

		user = reads ();
		}
	
	this (char[] user, char[][char[]] likes, char[][char[]] hates)
		{
		super (UserInterests);

		writes (user);
	
		uint n = likes.length;
		writei (likes.length);
		foreach (char[] thing ; likes)
			{
			writes (thing);
			}
		n = hates.length;
		writei (hates.length);
		foreach (char[] thing ; hates)
			{
			writes (thing);
			}
		}
	}
class UGetItemRecommendations : Message
	{		// An user wants to get recommendations
			// for a particular item
	char[] item;

	this (Stream s)
		{
		super (s);

		item = reads ();
		}
	
	this (char[] item)
		{
		super (ItemRecommendations);

		writes (item);
		}
	}

class UItemSimilarUsers : Message
	{		// An user wants to know who
			// likes a particular item
	char[] item;

	this (Stream s)
		{
		super (s);

		item = reads ();
		}
	
	this (char[] item)
		{
		super (ItemSimilarUsers);

		writes (item);
		}
	}

class USetRoomTicker : Message
	{		// Client sets a new ticker for a room
	char[] room;	// room name
	char[] tick;	// ticker content

	this (Stream s)
		{
		super (s);

		room = reads ();
		tick = reads ();
		}
	
	this (char[] room, char[] tick)
		{
		super (SetRoomTicker);

		writes (room);
		writes (tick);
		}
	}

class URoomSearch : Message
	{		// Client wants to send a search to all the users in the room
	char[] room;	// room name
	uint     token;	// search token
	char[] query;	// search string

	this (Stream s)
		{
		super (s);

		room  = reads ();
		token = readi ();
		query = reads ();
		}
	
	this (char[] room, uint  token, char[] query)
		{
		super (RoomSearch);

		writes (room);
		writei (token);
		writes (query);
		}
	}

class UUserPrivileges : Message
        {               // Client wants to know if someone has privileges
        char[] user;    // user name

        this (Stream s)
                {
                super (s);
        
                user = reads ();
                }
	
	this (char[] user)
		{
		super (UserPrivileges);

		writes (user);
		}
        }

class UAdminMessage : Message
	{		// An admin sends a message
	char[] mesg;	// the message

	this (Stream s)
		{
		super (s);

		mesg = reads ();
		}
	
	this (char[] mesg)
		{
		super (AdminMessage);

		writes (mesg);
		}
	}

class UGivePrivileges : Message
	{		// Client wants to give privileges to somebody else
	char[] user;	// user to give the privileges to
	uint    time;	// time to give

	this (Stream s)
		{
		super (s);

		user = reads ();
		time = readi ();
		}
	
	this (char[] user, uint time)
		{
		super (GivePrivileges);

		writes (user);
		writei (time);
		}
	}

class UCantConnectToPeer : Message
	{		// Client tells us he couldn't connect to someone
	uint  token;	// message token
	char[] user;	// user who requested the connection

	this (Stream s)
		{
		super (s);

		token = readi ();
		user  = reads ();
		}
	
	this (uint  token, char[] user)
		{
		super (CantConnectToPeer);

		writei (token);
		writes (user);
		}
	}

class SLogin : Message
	{	// If the login succeeded send the MOTD and the external IP of the client
		// if not, send the error message
	this (byte success, char[] mesg, uint  addr = 0, char[] password = null)
		{
		super (Login);
		
		writeb (success);	// success (0 = fail / 1 = success)
		writes (mesg);		// server message
		if (success)
			{
//                         derr.writefln (addr);
			writei (addr);	// external IP address of the client

			if (!(password is null))
				{	// the official  server now sends this md5sum
					// back to the client at login.  The official
					// client uses it to check if it is connected
					// to the official server.
				ubyte[16] digest;
				sum (digest, password);
				char[] sum;
				foreach (ubyte u ; digest)
					sum ~= format ("%02x", u);
				writes (sum);
				}
			}
		}
		
	byte   success;
	char[] mesg;
	uint     addr;
	
	this (Stream s)
		{
		super (s);

		success = readb ();
		mesg    = reads ();
		if (success) addr = readi ();
		}
	}

class SGetPeerAddress : Message
	{	// Send the address and port of user user
	this (char[] username, uint  address, uint  port)
		{
		super (GetPeerAddress);
		
		writes (username);	// username the address belongs to
		writei (address);	// IP address
		writei (port);		// port number
		}
	
	char[] username;
	uint     address;
	uint     port;

	this (Stream s)
		{
		super (s);

		username = reads ();
		address  = readi ();
		port     = readi ();
		}
	}

class SUserExists : Message
	{	// A client asked whether user user exists or not
	this (char[] user, byte exists)
		{
		super (UserExists);

		writes (user);   // username
		writeb (exists); // whether the user exists or not
		}
	
	char[] user;
	byte   exists;

	this (Stream s)
		{
		super (s);

		user   = reads ();
		exists = readb ();
		}
	}

class SAddUser : Message
	{	// A client asked to watch user, tell him if this user exists or not
		// the official server doesn't seem to be sending that anymore
	this (char[] user, byte exists)
		{
		super (AddUser);

		writes (user);   // username
		writeb (exists); // whether the user exists or not
		}
	
	char[] user;
	byte   exists;

	this (Stream s)
		{
		super (s);

		user   = reads ();
		exists = readb ();
		}
	}

class SGetUserStatus : Message
	{	// Send the status of user user
	this (char[] username, int status)
		{
		super (GetUserStatus);

		writes (username);	// username
		writei (status);	// user status (see the class User)
		}

	char[] username;
	int    status;

	this (Stream s)
		{
		super (s);

		username = reads ();
		status   = readi ();
		}
	}

class SSayChatroom : Message
	{	// User user has said mesg in room room
	this (char[] room, char[] user, char[] mesg)
		{
		super (SayChatroom);

		writes (room); // room the message comes from
		writes (user); // the user who said it
		writes (mesg); // what (s)he said
		}
	
	char[] room;
	char[] user;
	char[] mesg;

	this (Stream s)
		{
		super (s);

		room = reads ();
		user = reads ();
		mesg = reads ();
		}
	}

class SRoomList : Message
	{	// Send the list of rooms
	this (uint[char[]] rooms)
		{
		super (RoomList);
		
		writei (rooms.length);	// number of room names we will send
		foreach (char[] room ; rooms.keys)
			{	// list of all the rooms
			writes (room);
			}
		
		writei (rooms.length);	// number of user counts
		foreach (uint users ; rooms.values)
			{	// list of all the user counts, in the same order
			writei (users);
			}
		}
	
	uint[char[]] rooms;

	this (Stream s)
		{
		super (s);

		uint n = readi ();
		
		char[][] room_names;
		
		room_names.length = n;
		
		for (uint i = 0 ; i < n ; i++)
			{
			room_names[i] = reads ();
			}

		n = readi ();

		for (uint i = 0 ; i < n ; i++)
			{
			rooms[room_names[i]] = readi ();
			}
		}
	}

class SJoinRoom : Message
	{	// Give info on the room to a client who just joined it
	this (char[] room, char[][] usernames, int[char[]] statuses, uint[char[]] speeds, uint[char[]] download_numbers, uint[char[]] somethings, uint[char[]] shared_files, uint[char[]] shared_folders, uint[char[]] slots_full)
		{
		super (JoinRoom);

		writes (room);	// the room the user just joined
		uint n = usernames.length;

		writei (n);	// number of user names we will send
		foreach (char[] username ; usernames)
			{	// list of all the user names
			writes (username);
			}
		
		writei (n);	// number of user statuses we will send
		foreach (char[] username ; usernames)
			{	// list of all the user statuses
			writei (statuses        [username]);
			}
		
		writei (n);	// number of stats we will send
		foreach (char[] username ; usernames)
			{
			writei (speeds          [username]);	// speed of each user
			writei (download_numbers[username]);	// number of files downloaded ever
			writei (somethings      [username]);	// something ? 1789 is a good number
			writei (shared_files    [username]);	// nb of shared files
			writei (shared_folders  [username]);	// nb of shared folders
			}
		
		writei (n);	// number of slots records we will send...
		foreach (char[] username ; usernames)
			{	// list of nb of full slots for each user
			writei (slots_full      [username]);
			}
		}
	
	char[] room;
	char[][]    usernames;
	int[char[]] statuses;
	uint[char[]] speeds;
	uint[char[]] download_numbers;
	uint[char[]] somethings;
	uint[char[]] shared_files;
	uint[char[]] shared_folders;
	uint[char[]] slots_full;
	
	this (Stream s)
		{
		super (s);

		room = reads ();

		uint n = readi ();
		usernames.length = n;

		for (int i = 0 ; i < n ; i++)
			{
			usernames[i] = reads ();
			}

		n = readi ();

		for (uint i = 0 ; i < n ; i++)
			{
			statuses        [usernames[i]] = readi ();
			}

		n = readi ();

		for (uint i = 0 ; i < n ; i++)
			{
			speeds          [usernames[i]] = readi ();
			download_numbers[usernames[i]] = readi ();
			somethings      [usernames[i]] = readi ();
			shared_files    [usernames[i]] = readi ();
			shared_folders  [usernames[i]] = readi ();
			}

		n = readi ();

		for (uint i = 0 ; i < n ; i++)
			{
			slots_full      [usernames[i]] = readi ();
			}
		}
	}
	
class SLeaveRoom : Message
	{	// Tell a client he has to leave a room
	this (char[] room)
		{
		super (LeaveRoom);

		writes (room);	// the room the user left
		}
	
	char[] room;

	this (Stream s)
		{
		super (s);

		room = reads ();
		}
	}

class SUserJoinedRoom : Message
	{	// User user has joined the room room
	this (char[] room, char[] username, int status, uint speed, uint download_number, uint something, uint shared_files, uint shared_folders, uint slots_full)
		{
		super (UserJoinedRoom);

		writes (room);			// the room an user joined
		writes (username);		// name of the user who joined
		writei (status);		// status
		writei (speed);			// speed
		writei (download_number);	// download number ?
		writei (something);		// something ?
		writei (shared_files);		// shared files
		writei (shared_folders);	// shared folders
		writei (slots_full);		// slots full
		}
	
	char[] room;
	char[] username;
	int    status;
	uint    speed;
	uint    download_number;
	uint    something;
	uint    shared_files;
	uint    shared_folders;
	uint    slots_full;

	this (Stream s)
		{
		super (s);

		room		= reads ();
		username        = reads ();
		status          = readi ();
		speed           = readi ();
		download_number = readi ();
		something       = readi ();
		shared_files    = readi ();
		shared_folders  = readi ();
		slots_full      = readi ();
		}
	}

class SUserLeftRoom : Message
	{	// User user has left the room room
	this (char[] username, char[] room)
		{
		super (UserLeftRoom);

		writes (room);		// the room an user left
		writes (username);	// name of the user who left
		}

	char[] room;
	char[] username;

	this (Stream s)
		{
		super (s);

		room     = reads ();
		username = reads ();
		}
	}

class SConnectToPeer : Message
	{	// Ask a peer to connect back to user
	this (char[] username, char[] type, uint  address, uint  port, uint  token)
		{
		super (ConnectToPeer);

		writes (username);	// username of the peer to connect to
		writes (type);		// type of the connection ("F" if it's for a filetransfer, "P" otherwise)
		writei (address);	// IP address of the peer to connect to
		writei (port);		// port to use
		writei (token);		// message token
		}
	
	char[] username;
	char[] type;
	uint     address;
	uint     port;
	uint     token;

	this (Stream s)
		{
		super (s);

		username = reads ();
		type     = reads ();
		address  = readi ();
		port     = readi ();
		token    = readi ();
		}
	}

class SMessageUser : Message
	{	// Send the PM
	this (uint  id, uint  timestamp, char[] from, char[] content)
		{
		super (MessageUser);

		writei (id);		// message id
		writei (timestamp);	// timestamp (seconds since 1970)
		writes (from);		// sender
		writes (content);	// message content
		}
	
	uint     id;
	uint     timestamp;
	char[] from;
	char[] content;

	this (Stream s)
		{
		super (s);

		id        = readi ();
		timestamp = readi ();
		from      = reads ();
		content   = reads ();
		}
	}

class SFileSearch : Message
	{	// Send a filesearch
	this (char[] username, uint  token, char[] string)
		{
		super (FileSearch);

		writes (username);	// username of the one who is doing the search
		writei (token);		// search token
		writes (string);	// search string
		}
	
	char[] username;
	uint     token;
	char[] string;

	this (Stream s)
		{
		super (s);

		username = reads ();
		token    = readi ();
		string   = reads ();
		}
	}

class SServerPing : Message
	{	// Pong, in response to a client ping
	this ()
		{
		super (ServerPing);
		}
	}

class SGetUserStats : Message
	{	// Send the stats of user user
	this (char[] username, uint speed, uint download_number, uint something, uint shared_files, uint shared_folders)
		{
		super (GetUserStats);

		writes (username);		// user name
		writei (speed);			// speed (in B/s)
		writei (download_number);	// download number ?
		writei (something);		// something ?
		writei (shared_files);		// shared files
		writei (shared_folders);	// shared folders
		}
	
	char[] username;
	uint    speed;
	uint    download_number;
	uint    something;
	uint    shared_files;
	uint    shared_folders;

	this (Stream s)
		{
		super (s);

		username        = reads ();
		speed           = readi ();
		download_number = readi ();
		something       = readi ();
		shared_files    = readi ();
		shared_folders  = readi ();
		}
	}

class SGetRecommendations : Message
	{	// Send the list of recommendations for this client
	this (uint [char[]] list)	// list[artist] = level
		{
		super (GetRecommendations);

		writei (list.length);	// if you can't guess, stop reading now !
		foreach (char[] artist, uint  level ; list)
			{
			writes (artist);	// artist name
			writei (level);		// « level » of recommendation
			}
		}
	
	uint [char[]] list;

	this (Stream s)
		{
		super (s);

		uint  n = readi ();
		for (uint  i = 0 ; i < n ; i++)
			{
			char[] artist = reads ();
			uint     level  = readi ();

			list[artist] = level;
			}
		}
	}

class SGetGlobalRecommendations : Message
	{	// Send the list of global recommendations
		// the code is exactly the same as for GetRecommendations.
	this (uint [char[]] list)
		{
		super (GlobalRecommendations);

		writei (list.length);	// if you can't guess, you should have stopped several lines ago...
		foreach (char[] artist, uint  level ; list)
			{
			writes (artist);	// artist name
			writei (level);		// « level » of recommendation
			}
		}
	
	uint [char[]] list;

	this (Stream s)
		{
		super (s);

		uint  n = readi ();
		for (uint  i = 0 ; i < n ; i++)
			{
			char[] artist = reads ();
			uint     level  = readi ();

			list[artist] = level;
			}
		}
	}

class SRelogged : Message
	{	// Tell a client he has just logged from elsewhere before disconnecting it
	this ()
		{
		super (Relogged);
		}
	}

class SUserSearch : Message
	{	// User user has sent a buddy search to a client
	this (char[] user, uint  token, char[] query)
		{
		super (UserSearch);

		writes (user);		// name of the user who sent the search
		writei (token);		// search token
		writes (query);		// search string
		}
	
	char[] user;
	uint     token;
	char[] query;

	this (Stream s)
		{
		super (s);

		user   = reads ();
		token  = readi ();
		query  = reads ();
		}
	}

class SRoomAdded : Message
	{	// A room has been created
	this (char[] room)
		{
		super (RoomAdded);

		writes (room);	// name of the room
		}

	char[] name;

	this (Stream s)
		{
		super (s);

		name = reads ();
		}
	}

class SRoomRemoved : Message
	{	// A room has been removed
	this (char[] room)
		{
		super (RoomRemoved);

		writes (room);	// name of the room
		}

	char[] name;

	this (Stream s)
		{
		super (s);

		name = reads ();
		}
	}

class SAdminMessage : Message
	{	// Send an admin message
	this (char[] message)
		{
		super (AdminMessage);

		writes (message);	// the message
		}

	char[] message;

	this (Stream s)
		{
		super (s);

		message = reads ();
		}
	}

class SCheckPrivileges : Message
	{	// Tell a client how many seconds of privileges he has left
	this (uint  time)
		{
		super (CheckPrivileges);

		writei (time);		// time left
		}

	uint  time;

	this (Stream s)
		{
		super (s);

		time = readi ();
		}
	}

class SSimilarUsers : Message
	{	// Send a list of users with similar tastes
	this (uint [char[]] list)
		{
		super (SimilarUsers);

		writei (list.length);
		foreach (char[] user, uint  weight ; list)
			{
			writes (user);
			writei (weight);
			}
		}

	uint [char[]] list;

	this (Stream s)
		{
		super (s);

		uint  n = readi ();
		for (uint  i = 0 ; i < n ; i++)
			{
			char[] user   = reads ();
			uint     weight = readi ();

			list[user] = weight;
			}
		}
	}

class SGetItemRecommendations : Message
	{	// Send a list of recommendations for a particular item
	this (char[] item, uint [char[]] list)
		{
		super (ItemRecommendations);

		writes (item);
		writei (list.length);
		foreach (char[] item, uint  weight ; list)
			{
			writes (item);
			writei (weight);
			}
		}

	uint [char[]] list;

	this (Stream s)
		{
		super (s);

		uint  n = readi ();
		for (uint  i = 0 ; i < n ; i++)
			{
			char[] item   = reads ();
			uint     weight = readi ();

			list[item] = weight;
			}
		}
	}

class SItemSimilarUsers : Message
	{	// Send a list of users who like an item
	this (char[] item, char[][] list)
		{
		super (ItemSimilarUsers);

		writes (item);
		writei (list.length);
		foreach (char[] user ; list)
			{
			writes (user);
			}
		}

	char[][] list;

	this (Stream s)
		{
		super (s);

		uint  n = readi ();
		list.length = n;
		for (uint  i = 0 ; i < n ; i++)
			{
			list[i] = reads ();
			}
		}
	}

class SRoomTicker : Message
	{	// Send the ticker of room room
	this (char[] room, char[][char[]] tickers)
		{
		super (RoomTicker);

		writes (room);			// name of the room
		writei (tickers.length);	// number of tickers
		foreach (char[] user, char[] ticker ; tickers)
			{
			writes (user);		// user name
			writes (ticker);	// ticker content
			}
		}

	char[] room;
	char[][char[]] tickers;

	this (Stream s)
		{
		super (s);
		
		room = reads ();
		
		uint  n = readi ();
		for (uint  i = 0 ; i < n ; i++)
			{
			char[] user   = reads ();
			char[] ticker = reads ();

			tickers[user] = ticker;
			}
		}
	}

class SRoomTickerAdd : Message
	{	// A ticker has been added to the room room by the user user
	this (char[] room, char[] user, char[] ticker)
		{
		super (RoomTickerAdd);

		writes (room);		// name of the room
		writes (user);		// user name
		writes (ticker);	// ticker content
		}
	
	char[] room;
	char[] user;
	char[] ticker;

	this (Stream s)
		{
		super (s);

		room   = reads ();
		user   = reads ();
		ticker = reads ();
		}
	}

class SRoomTickerRemove : Message
	{	// User user has removed his ticker from the room room
	this (char[] room, char[] user)
		{
		super (RoomTickerRemove);

		writes (room);		// name of the room
		writes (user);		// user name
		}
	
	char[] room;
	char[] user;

	this (Stream s)
		{
		super (s);

		room   = reads ();
		user   = reads ();
		}
	}

class SRoomSearch : Message
	{	// User user has sent a room search
	this (char[] user, uint  token, char[] query)
		{
		super (RoomSearch);

		writes (user);		// name of the user who sent the search
		writei (token);		// search token
		writes (query);		// search string
		}
	
	char[] user;
	uint     token;
	char[] query;

	this (Stream s)
		{
		super (s);

		user   = reads ();
		token  = readi ();
		query  = reads ();
		}
	}

class SUserPrivileges : Message
	{	// Send the privileges status of user
	this (char[] username, uint  privileges)
		{
		super (UserPrivileges);

		writei (privileges);	// user privileges
		writes (username);	// user name
		}

	uint     privileges;
	char[] username;

	this (Stream s)
		{
		super (s);

		privileges = readi ();
		username   = reads ();
		}
	}

class SCantConnectToPeer : Message
	{	// A connection couldn't be established for some message
	this (uint  token)
		{
		super (CantConnectToPeer);

		writei (token);	// token of the message
		}

	uint  token;

	this (Stream s)
		{
		super (s);

		token = readi ();
		}
	}

class SServerInfo : Message
	{	// Specific to Soulfind, info about the server
		// uint     : number of fields
		// string : field name
		// string : field value
		// string : field name
		// ...
	this (char[][char[]] info)
		{
		super (ServerInfo);
		
		writei (info.length);	// number of fields being sent

		foreach (char[] field, char[] value ; info)
			{
			writes (field);
			writes (value);
			}
		}

	char[][char[]] info;

	this (Stream s)
		{
		super (s);

		uint  n = readi ();
		for (uint  i = 0 ; i < n ; i++)
			{
			char[] field = reads ();
			char[] value = reads ();

			info[field]  = value;
			}
		}
	}

void send_byte (OutBuffer b, byte o)
	{
	b.write (o);
	}

void send_int (OutBuffer b, int i)
	{
	b.write (i);
	}
void send_uint (OutBuffer b, uint i)
	{
	b.write (i);
	}
void send_string (OutBuffer b, char[] str)
	{
	b.write (str.length);
	b.write (str);
	}

